﻿#define  _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	int* p = &arr[0];
//	printf("&arr[0] = %p\n", &arr[0]);
//	printf("arr = %p\n", arr);  // 发现这两个地址都一样
//	return 0;
//}

//我们发现数组名和数组⾸元素的地址打印出的结果⼀模⼀样，数组名就是数组⾸元素(第⼀个元素)的地
//址。

//这时候有同学会有疑问？数组名如果是数组⾸元素的地址，那下⾯的代码怎么理解呢？
#include <stdio.h>
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	printf("%d\n", sizeof(arr));
//	return 0;
//}

//输出的结果是：40，如果arr是数组⾸元素的地址，那输出应该的应该是4 / 8才对。
//其实数组名就是数组⾸元素(第⼀个元素)的地址是对的，但是有两个例外：
//• sizeof(数组名)，sizeof中单独放数组名，这⾥的数组名表⽰整个数组，计算的是整个数组的⼤⼩，
//单位是字节
//• & 数组名，这⾥的数组名表⽰整个数组，取出的是整个数组的地址（整个数组的地址和数组⾸元素
//的地址是有区别的）
//除此之外，任何地⽅使⽤数组名，数组名都表⽰⾸元素的地址。
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	printf("&arr[0] = %p\n", &arr[0]);
//	printf("arr = %p\n", arr);
//	printf("&arr = %p\n", &arr);  //这三个的结果都是一样 前面两个表示的都是数组首元素的地址
//	//第三个是数组的地址 一个数组 数组首元素的地址 就是整个数组的地址 都是从这开始
//	// 指针类型决定了指针的差异
//	return 0;
//}

//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	printf("&arr[0] = %p\n", &arr[0]);
//	printf("&arr[0]+1 = %p\n", &arr[0]+1); //跳过了四个字节 一个整型元素就是4个字节
//
//	printf("arr = %p\n", arr);
//	printf("arr + 1 = %p\n", arr+1); //跳过了四个字节 
//
//	printf("&arr = %p\n", &arr);   
//	printf("&arr+1 = %p\n", &arr+1);  // 跳过了0x28 个字节 这是16进制  转换为十进制也就是40个字节
//	//跳过了整个数组
//	return 0;
//}

//利用指针给一维数组输入值 并输出打印

//这是利用下标访问数组
//int main()
//{
//	int arr[] = { 0 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	//给数组输入值
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		scanf("%d", &arr[i]);
//	}
//	//输出
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//	return 0;
//}

//利用指针访问一维数组
//int main()
//{
//	int arr[10] = { 0 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	//给数组输入值
//	int i = 0;
//	int * p = &arr[0];
//	for (i = 0; i < sz; i++)
//	{
//		scanf("%d", p+i); //p+i是下标为i元素的地址
//	}
//	//输出
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *(p + i));
//	}
//	return 0;
//}
//
//
//int main()
//{
//	int arr[10] = { 0 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	//给数组输入值
//	int i = 0;
//	int* p = arr;
//	for (i = 0; i < sz; i++)
//	{
//		scanf("%d", p + i); //p+i是下标为i元素的地址
//	}
//	//输出
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *(p + i));
//	}
//	return 0;
//}

//int main()
//{
//	int arr[10] = { 0 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	//给数组输入值
//	int i = 0;
//	int* p = arr;
//	for (i = 0; i < sz; i++)
//	{
//		scanf("%d", arr+ i); //p+i是下标为i元素的地址
//	}
//	//输出
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *(p + i));
//	}
//	return 0;
//}
//
//int main()
//{
//	int arr[10] = { 0 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	//给数组输入值
//	int i = 0;
//	int* p = arr;
//	for (i = 0; i < sz; i++)
//	{
//		scanf("%d", arr + i); //p+i是下标为i元素的地址
//	}
//	//输出
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *(arr + i));
//	}
//	return 0;
//}

// *(arr+i) <===> arr[i] 后面这种写法编译器计算的时候也会转换为前面的那种来进行计算
// *(i+arr)这个也可以  我们也可以写为i[arr]
// []只是一个操作符而已  就比如a+b和b+a
//int main()
//{
//	int arr[10] = { 0 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	//给数组输入值
//	int i = 0;
//	int* p = arr;
//	for (i = 0; i < sz; i++)
//	{
//		scanf("%d", arr + i); //p+i是下标为i元素的地址
//	}
//	//输出
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", i[arr]);
//	}
//	return 0;
//}

// 数组就是数组，是一块连续的空间，是可以存放一个或多个数组的  
// 指针变量是一个变量，是可以存放地址的变量
// 数组和指针不是一回事 但是可以使用指针来访问数组
// 为什么可以使用指针访问数组？
// 1. 数组在内存中是连续存放的
// 2. 指针的运算很方便的可以遍历数组,取出数组的内容


// 一维数组传参的本质

//void test(int arr[10]) // 其实这个形参是int* arr
//{
//	int sz2 = sizeof(arr) / sizeof(arr[0]);
//	printf("sz2 = %d \n", sz2); //1
//}
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	int sz1 = sizeof(arr) / sizeof(arr[0]);
//	printf("sz1 = %d\n", sz1); //10
//	test(arr);
//	return 0;
//}

//这就要学习数组传参的本质了，上个⼩节我们学习了：数组名是数组⾸元素的地址；那么在数组传参
//的时候，传递的是数组名，也就是说本质上数组传参本质上传递的是数组⾸元素的地址。
//所以函数形参的部分理论上应该使⽤指针变量来接收⾸元素的地址。那么在函数内部我们写
//sizeof(arr) 计算的是⼀个地址的⼤⼩（单位字节）⽽不是数组的⼤⼩（单位字节）。正是因为函
//数的参数部分是本质是指针，所以在函数内部是没办法求的数组元素个数的。
//总结：⼀维数组传参，形参的部分可以写成数组的形式，也可以写成指针的形式。前者是为了方便初学者理解，后者才是本质
// 形参即使写成数组的形式，本质上还是一个指针变量

//void test(int arr[],int sz) // 其实这个形参是int* arr
//{
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//}

//void test(int * arr, int sz) // 其实这个形参是int* arr
//{
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *(arr + i));  // arr[i] == *(arr+i)
//		//printf("%d ", arr[i]);  // arr[i] == *(arr+i)
//	}
//}
//
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	int sz1 = sizeof(arr) / sizeof(arr[0]);
//	printf("sz1 = %d\n", sz1); //10
//	test(arr,sz1);
//	return 0;
//}

//冒泡排序的核⼼思想就是：两两相邻的元素进⾏⽐较,不满足顺序就交换，满足顺序就找下一对
// 一套冒泡排序可以让一个数来到最后 用完一套再一套 让其满足升序
// 10个元素 需要进行9套冒泡排序 n个元素 n-1套

//void input(int* arr, int sz)
//{
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		scanf("%d", arr + i);
//	}
//}
//
//void bubble_sort(int* arr, int sz)
//{
//	//确定冒泡排列趟数
//	int i = 0;
//	for (i = 0; i < sz - 1; i++)
//	{
//		// 每一趟内部比较
//		int j = 0;
//		for (j = 0; j <sz-1-i; j++)  // sz-1-i 第二趟就比较8个
//		{
//			if (arr[j] > arr[j + 1])
//			{
//				// 交换
//				int tmp = arr[j];
//				arr[j] = arr[j + 1];
//				arr[j + 1] = tmp;
//			}
//		}
//	}
//}
//void print_arr(int arr[],int sz)
//{
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//}
//int main()
//{
//	int arr[10] = { 0 };
//	//输入一些乱序的值
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	input(arr, sz);
//	// 排序 - 写一个函数完成数组的排序，排成升序
//	bubble_sort(arr,sz); // 使用冒泡排序
//	print_arr(arr, sz);
//	return 0;
//}


// 优化
//void input(int* arr, int sz)
//{
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		scanf("%d", arr + i);
//	}
//}
//
//void bubble_sort(int* arr, int sz)
//{
//	//确定冒泡排列趟数
//	int i = 0;
//	for (i = 0; i < sz - 1; i++)
//	{
//		int flag = 1; //假设已经满足顺序
//		// 每一趟内部比较
//		int j = 0;
//		for (j = 0; j < sz - 1 - i; j++)  // sz-1-i 第二趟就比较8个
//		{
//			if (arr[j] > arr[j + 1])
//			{
//				flag = 0; // 还不是有序
//				// 交换
//				int tmp = arr[j];
//				arr[j] = arr[j + 1];
//				arr[j + 1] = tmp;
//			}
//		}
//		if (flag == 1)
//		{
//			break;
//		}
//	}
//}
//void print_arr(int arr[], int sz)
//{
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//}
//int main()
//{
//	int arr[10] = { 0 };
//	//输入一些乱序的值
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	input(arr, sz);
//	// 排序 - 写一个函数完成数组的排序，排成升序
//	bubble_sort(arr, sz); // 使用冒泡排序
//	print_arr(arr, sz);
//	return 0;
//}

//指针变量也是变量，是变量就有地址，那指针变量的地址存放在哪⾥？
//这就是 ⼆级指针 。

//int main()
//{
//	int a = 10;
//
//	int* pa = &a; // pa是一级指针变量
//
//	int** ppa = &pa; // ppa就是二级指针变量  int * *  *说明ppa是指针变量  int*是指ppa指向的pa的类型是int*
//
//	return 0;
//}

//对于⼆级指针的运算有：
//•* ppa 通过对ppa中的地址进⾏解引⽤，这样找到的是 pa ，* ppa 其实访问的就是 pa .
//int b = 20;
//*ppa = &b;//等价于 pa = &b;
//
//• * *ppa 先通过 * ppa 找到 pa, 然后对 pa 进⾏解引⽤操作：* pa ，那找到的是 a .
//** ppa = 30;
//等价于*pa = 30;
//等价于a = 30;

//int main()
//{
//	int a = 10;
//	int* pa = &a; //pa是一级指针变量
//	int** ppa = &pa; // ppa就是二级指针变量
//	printf("%d\n", **ppa);
//	**ppa = 200;
//	printf("%d\n", a);
//	return 0;
//}

// 二级指针和二维数组没有对应关系

//指针数组是指针还是数组？
//我们类⽐⼀下，整型数组，是存放整型的数组，字符数组是存放字符的数组。
//那指针数组呢？是存放指针的数组。
//int main()
//{
//	int a = 10;
//	int b = 20;
//	int c = 30;
//	//int* pa = &a;
//	//int* pb = &b; //麻烦
//	int* arr[3] = { &a,&b,&c };
//	// 0 1 2 作为下标
//	int i = 0;
//	for (i = 0; i < 3; i++)
//	{
//		printf("%d ", *(arr[i])); // 得到地址解引用 即可得到值
//	}
//	return 0;
//}

// 使用指针数组模拟二维数组
//int main()
//{
//	int arr1[5] = { 1,2,3,4,5 };
//	int arr2[5] = { 2,3,4,5,6 };
//	int arr3[5] = { 3,4,5,6,7 };
//	int * arr[] = { arr1, arr2, arr3 }; // 三个整型元素的地址放到指针数组里面  数组名就是数组第一个元素的地址
//	// 指针数组的每个元素是地址，⼜可以指向⼀块区域
//	int i = 0;
//	for (i = 0; i < 3; i++)
//	{
//		int j = 0;
//		for (j = 0; j < 5; j++)
//		{
//			printf("%d ", arr[i][j]);  // *(*(arr+i)+j)
//		}
//		printf("\n"); // 打印完一行换行
//	}
//	return 0;
//}


//上述的代码模拟出⼆维数组的效果，实际上并⾮完全是⼆维数组，因为每⼀⾏并⾮是连续的

// 字符指针变量
//int main()
//{
//	char ch = 'w';
//	char* pc = &ch;
//	*pc = 's';
//	printf("%c\n", *pc);
//	return 0;
//}

//int main()
//{
//	const char* p = "abcdef";  // 后面这个叫常量字符串  放到同一片空间里面 并且不能修改
//	//这里的赋值是将字符串中首字符的地址赋给了p
//	printf("%c\n", *p); //a
//	printf("%s\n", p); // 使用%s打印字符串的时候，只需要提供首字符的地址就行，不要解引用
//
//	*p = 'q'; //不能修改 会引发程序异常 所以可以使用const修饰 这样这里直接就会报错，运行不了程序
//
//	char arr[] = "abcdef"; // 创建一个数组  数组可以修改
//	//char* p = arr;
//	return 0;
//}
